package cc.rengu.igas.route.core.realize.impl;

import cc.rengu.chargecalc.mchntfee.CalcMchntFee;
import cc.rengu.chargecalc.mchntfee.dao.FeeInfoMapper;
import cc.rengu.chargecalc.mchntfee.dao.impl.FeeInfoMapperImpl;
import cc.rengu.chargecalc.mchntfee.entity.FeeInfoObj;
import cc.rengu.chargecalc.mchntfee.util.Database.ConnectDB;
import cc.rengu.chargecalc.mchntfee.util.Database.DatabaseOpr;
import cc.rengu.igas.route.common.constant.SmartRouteParamConstant;
import cc.rengu.igas.route.common.dao.WeightCfgMapper;
import cc.rengu.igas.route.common.dao.impl.WeightCfgMapperImpl;
import cc.rengu.igas.route.common.entity.DyncDataStatic;
import cc.rengu.igas.route.common.entity.RuleCfg;
import cc.rengu.igas.route.common.entity.WeightCfg;
import cc.rengu.igas.route.common.enums.MustSignEnum;
import cc.rengu.igas.route.common.enums.RouteTxnNumEnum;
import cc.rengu.igas.route.common.enums.SignedStatusEnum;
import cc.rengu.igas.route.common.enums.SmartRouteRspEnums;
import cc.rengu.igas.route.common.util.IPv4Util;
import cc.rengu.igas.route.core.helper.RouterHelper;
import cc.rengu.igas.route.core.model.*;
import cc.rengu.igas.route.core.realize.RouterChoice;
import cc.rengu.igas.route.core.realize.payment.CheckVerificaElement;
import cc.rengu.igas.route.facade.bean.PayerInfo;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.dao.ChannelCallCfgMapper;
import cc.rengu.oltp.service.common.dao.DstChannelInfoMapper;
import cc.rengu.oltp.service.common.dao.DstTxnAuthCfgMapper;
import cc.rengu.oltp.service.common.dao.SignAcctInfoMapper;
import cc.rengu.oltp.service.common.dao.impl.ChannelCallCfgMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.DstChannelInfoMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.DstTxnAuthCfgMapperImpl;
import cc.rengu.oltp.service.common.dao.impl.SignAcctInfoMapperImpl;
import cc.rengu.oltp.service.common.entity.*;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.service.realize.SysParamService;
import cc.rengu.oltp.service.realize.impl.SysParamServiceImpl;
import cc.rengu.oltp.utility.util.AmountUtil;
import cc.rengu.oltp.utility.util.DateUtil;
import cc.rengu.oltp.utility.util.RedisUtil;
import cc.rengu.oltp.utility.util.StringUtil;
import cc.rengu.utility.cache.UnifiedCache;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import com.alibaba.fastjson.JSON;

import java.sql.Connection;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 智能路由实现类
 *
 * @author xionglz
 * @version 1.0
 * @date 2020-03-23
 */
public class WeightCfgRouterChoiceImpl implements RouterChoice {
    private static final Double zeroD = Double.parseDouble("0.000");
    private RgLogger rglog = RgLog.getLogger(this.getClass().getName());

    private Double feeMinD;
    private Map<String, String> mapFee;
    private boolean feeIsZero = false;
    private CompleteSmartRouteInfo completeSmartRouteInfo;

    /**
     * 智能路由选择
     *
     * @param ruleCfg   路由规则入口配置
     * @param routerReq 路由请求
     * @return 路由成功返回按照得分从高到底排序的可能的通道，null或者size为0路由失败
     * @throws Exception 其他异常
     */
    @Override
    public List<ProdInfoBean> routerChoice(RuleCfg ruleCfg, RouterReq routerReq) throws Exception {
        //1. 装载配置信息
        loadSmartCfg(ruleCfg, routerReq);
        //2. 代收交易才需要判断签约状态与验证要素是否齐全
        if (RouteTxnNumEnum.PAYMENT.getTxnGroup().equals(routerReq.getTxnGroup())) {
            calculateSignedCheckVericaElements(routerReq);
        }
        //2. 计算费用，设置到map中
        calculateCost(routerReq.getTransAmt());
        //4. 循环计算得分
        List<ProdInfoBean> listProdInfo = getAllScore(routerReq);
        //5. 查询模式，返回得分列表
        //5.1.listProdInfo经过上面的过滤不可能存在不需要签约且要素不齐全的通道;只有可能为空,为空则返回失败
        //5.2.获取listProdInfo中需要签约的条数,已签约的条数,不需要签约(不需要签约的验证要素一定是齐全的)的条数
        if (listProdInfo.isEmpty()) {
            rglog.error("无可用通道");
            throw new BizException(SmartRouteRspEnums.NO_AVAILABLE_DST_CHANNEL_ERROR.getRespCode(),
                    SmartRouteRspEnums.NO_AVAILABLE_DST_CHANNEL_ERROR.getRespDesc());
        }

        //7. 进行排序
        listProdInfo.sort((o1, o2) -> -Double.compare(Double.parseDouble(o1.getPriority()), Double.parseDouble(o2.getPriority())));
        listProdInfo.forEach(item -> rglog.debug("通道{},得分{}", item.getDstChannelId(), item.getPriority()));
        //8. 若通道数量大于等于2，则只判断头两条通道的得分是否一致，如果一致则根据其优先级在优先级高的得分+1
        procEqualScore(listProdInfo, ruleCfg);
        //9. 直接模式需要计算最优通道
        if (!RouteTxnNumEnum.ROUTER_QUERY.getTxnNum().equals(routerReq.getTxnNum())) {
            ProdInfoBean prodInfoMax = listProdInfo.get(0);
            listProdInfo.clear();
            listProdInfo.add(prodInfoMax);
        }
        return listProdInfo;
    }

    /**
     * 装载智能路由配置
     *
     * @param ruleCfg 路由配置的入口信息
     * @throws Exception 失败
     */
    private void loadSmartCfg(RuleCfg ruleCfg, RouterReq routerReq) throws Exception {
        //查询智能路由权重配置
        WeightCfg weightCfg = null;
        String key = SmartRouteParamConstant.ROUTE_CONF_SMART + ":" + ruleCfg.getInstId() + ":" + ruleCfg.getRuleId();
        try {
            weightCfg = JSON.parseObject(RedisUtil.onceGet(key), WeightCfg.class);
        } catch (Exception e) {
            rglog.warn("redis中未查询到智能路由配置<{}><{}>", ruleCfg.getInstId(), ruleCfg.getRuleId());
        }
        if (null == weightCfg) {
            WeightCfgMapper weightCfgMapper = new WeightCfgMapperImpl();
            weightCfg = weightCfgMapper.getWeightCfgList(ruleCfg.getInstId(), ruleCfg.getRuleId());
        }
        Optional.ofNullable(weightCfg).orElseThrow(() -> {
            rglog.error("未找到智能路由权重配置，<instId:{}>,<ruleId:{}>", ruleCfg.getInstId(), ruleCfg.getRuleId());
            return new BizException(SmartRouteRspEnums.CONFIG_ERROR.getRespCode(), SmartRouteRspEnums.CONFIG_ERROR.getRespDesc());
        });
        completeSmartRouteInfo = new CompleteSmartRouteInfo(weightCfg);
        //检查交易渠道在能力开放完成，检查交易通道在智能路由完成
        //根据机构id,源系统标识，交易码，业务分类查询通道服务调用配置表
        RouteTxnNumEnum txnNumEnum = RouteTxnNumEnum.getInstanceFromTxnGroup(ruleCfg.getTxnGroup());
        Optional.ofNullable(txnNumEnum).orElseThrow(() -> {
            rglog.warn("<{}>不支持的业务类型", ruleCfg.getTxnGroup());
            return new BizException(SmartRouteRspEnums.CONFIG_ERROR.getRespCode(), SmartRouteRspEnums.CONFIG_ERROR.getRespDesc());
        });
        //查询redis，不存在或者失败再降级查询数据库
        ChannelCallCfgMapper channelCallCfgMapper = new ChannelCallCfgMapperImpl();
        List<ChannelCallCfg> channelCallCfgs = channelCallCfgMapper.selectChannelCallCfgByTxnNum(ruleCfg.getInstId(), SmartRouteParamConstant.IGRT,
                SmartRouteParamConstant.ALL, txnNumEnum.getTxnNum(), SmartRouteParamConstant.STRING_TWO_ZERO);
        Optional.ofNullable(channelCallCfgs).orElseThrow(() -> {
            rglog.error("获取交易码映射表失败");
            return new BizException(SmartRouteRspEnums.CONFIG_ERROR.getRespCode(), SmartRouteRspEnums.CONFIG_ERROR.getRespDesc());
        });
        for (ChannelCallCfg channelCallCfg : channelCallCfgs) {
            rglog.warn("该通道<{}：{}：{}>", channelCallCfg.getInstId(), channelCallCfg.getCallChannelId(), channelCallCfg.getCallChannelTxn());
            //获取通道信息
            DstChannelInfo dstChannelInfo = null;
            String dstChannelInfoKey = channelCallCfg.getInstId().trim() + channelCallCfg.getCallChannelId().trim();
            try {
                dstChannelInfo = JSON.parseObject(RedisUtil.hashGet(AppParamConstant.CHANNEL_ISS_INFO_CACHE, dstChannelInfoKey), DstChannelInfo.class);
            } catch (Exception e) {
                rglog.warn("redis中未查询到key<{}>通道信息", dstChannelInfoKey);
            }
            if (null == dstChannelInfo) {
                DstChannelInfoMapper dstChannelInfoMapper = new DstChannelInfoMapperImpl();
                dstChannelInfo = dstChannelInfoMapper.selectDstChannelInfoByPrimaryKey(channelCallCfg.getInstId().trim(), channelCallCfg.getCallChannelId().trim());
            }
            if (null == dstChannelInfo) {
                //未配置该通道
                rglog.warn("该通道<{}：{}>未配置通道信息", channelCallCfg.getInstId(), channelCallCfg.getCallChannelId());
                continue;
            }
            //排除本行通道
            if (SmartRouteParamConstant.ESB.equalsIgnoreCase(dstChannelInfo.getDstChannelId()) ||
                    SmartRouteParamConstant.ESBMS.equalsIgnoreCase(dstChannelInfo.getDstChannelId())) {
                continue;
            }
            //获取通道交易权限信息
            DstTxnAuthCfg dstTxnAuthCfg = null;
            String dstTxnAuthKey = SmartRouteParamConstant.ROUTE_CONF_DST_TXN_AUTH + ":" + channelCallCfg.getInstId() + ":" + channelCallCfg.getCallChannelId() + ":" + channelCallCfg.getCallChannelTxn();
            try {
                dstTxnAuthCfg = JSON.parseObject(RedisUtil.onceGet(dstTxnAuthKey), DstTxnAuthCfg.class);
            } catch (Exception e) {
                rglog.warn("redis中未查询到key<{}>通道交易权限信息", dstTxnAuthKey);
            }
            if (null == dstTxnAuthCfg) {
                //查询数据库
                DstTxnAuthCfgMapper dstTxnAuthCfgMapper = new DstTxnAuthCfgMapperImpl();
                dstTxnAuthCfg = dstTxnAuthCfgMapper.selectDstTxnAuthCfgByPrimaryKey(channelCallCfg.getInstId(), channelCallCfg.getCallChannelId(), channelCallCfg.getCallChannelTxn());
            }
            if (null == dstTxnAuthCfg) {
                rglog.warn("该通道<{}：{}：{}>没有配置支持智能路由的通道交易", channelCallCfg.getInstId(), channelCallCfg.getCallChannelId(), channelCallCfg.getCallChannelTxn());
                continue;
            }
            //获取通道交易对应的实现封装
            //根据通道标识，通道交易码查询系统参数表获取对应实现类
            String type = SmartRouteParamConstant.PARAM_TYPE_PREFIX_CHANNEL_INSTANCE + dstChannelInfo.getDstChannelId();
            SysParamService sysParamService = new SysParamServiceImpl();
            String txnNum;
            //由于签约使用的是代收的路由方式，因此签约需要特殊处理
            if (RouteTxnNumEnum.PAYMENT_SIGN.getTxnNum().equals(routerReq.getTxnNum()) ||
                    RouteTxnNumEnum.PAYMENT_SIGN_VERIFY.getTxnNum().equals(routerReq.getTxnNum())) {
                txnNum = routerReq.getTxnNum();
            } else {
                txnNum = channelCallCfg.getTxnNum();
            }
            SysParam sysParam = sysParamService.getSysParamInfo(dstChannelInfo.getInstId(), type, txnNum);
            if (sysParam == null || StringUtil.isEmptyOrNull(sysParam.getParamValue()) || !SmartRouteParamConstant.YES.equalsIgnoreCase(sysParam.getParamStatus())) {
                rglog.warn("根据通道标识<channelId:{}>，渠道交易码<{}>未找到机构<{}>客户化实现类或者通用法人实现类", type, txnNum, dstChannelInfo.getInstId());
                continue;
            }
            CompleteRouteInfoBean completeRouteInfoBean = new CompleteRouteInfoBean(dstChannelInfo, channelCallCfg, dstTxnAuthCfg);
            completeRouteInfoBean.setDstChannelTxnImplClassName(sysParam.getParamValue());
            //判断通道通用性阻断条件
            DstChannelFilterModel dstChannelFilterModel = new DstChannelFilterModel();
            dstChannelFilterModel.setInstId(routerReq.getInstId());
            dstChannelFilterModel.setTxnNum(routerReq.getTxnNum());
            dstChannelFilterModel.setDstChannelId(completeRouteInfoBean.getDstChannelInfo().getDstChannelId());
            //贷记使用收款方信息
            if (RouteTxnNumEnum.CREDIT.getTxnGroup().equals(routerReq.getTxnGroup())) {
                if (null != routerReq.getPayeeInfo()) {
                    if (!cc.rengu.utility.base.StringUtil.isNullorEmpty(routerReq.getPayeeInfo().getAcctType())) {
                        dstChannelFilterModel.setAcctType(routerReq.getPayeeInfo().getAcctType());
                    }
                    if (!cc.rengu.utility.base.StringUtil.isNullorEmpty(routerReq.getPayeeInfo().getOpenBankNo())) {
                        dstChannelFilterModel.setOpenBankNo(routerReq.getPayeeInfo().getOpenBankNo());
                    }
                    if (!cc.rengu.utility.base.StringUtil.isNullorEmpty(routerReq.getPayeeInfo().getAcctNo())) {
                        dstChannelFilterModel.setAcctNo(routerReq.getPayeeInfo().getAcctNo());
                    }
                }
            }
            //借记及鉴权使用付款方信息
            else {
                if (null != routerReq.getPayerInfo()) {
                    if (!cc.rengu.utility.base.StringUtil.isNullorEmpty(routerReq.getPayerInfo().getAcctType())) {
                        dstChannelFilterModel.setAcctType(routerReq.getPayerInfo().getAcctType());
                    }
                    if (!cc.rengu.utility.base.StringUtil.isNullorEmpty(routerReq.getPayerInfo().getOpenBankNo())) {
                        dstChannelFilterModel.setOpenBankNo(routerReq.getPayerInfo().getOpenBankNo());
                    }
                    if (!cc.rengu.utility.base.StringUtil.isNullorEmpty(routerReq.getPayerInfo().getAcctNo())) {
                        dstChannelFilterModel.setAcctNo(routerReq.getPayerInfo().getAcctNo());
                    }
                }
            }
            dstChannelFilterModel.setTransAmt(routerReq.getTransAmt());
            dstChannelFilterModel.setTxnGroup(routerReq.getTxnGroup());
            if (!RouterHelper.dstChannelFilter(dstChannelFilterModel, completeRouteInfoBean)) {
                rglog.warn("通道<{}>检查不通过", dstChannelFilterModel.getDstChannelId());
            } else {
                if (RouteTxnNumEnum.PAYMENT_SIGN.getTxnGroup().equals(dstChannelFilterModel.getTxnGroup())) {
                    //TODO 若为签约交易，检查是否签约，未签约的检查签约要素是否齐全
                    if (!checkVerificationElements(dstChannelFilterModel.getDstChannelId(), routerReq.getPayerInfo())) {
                        rglog.warn("通道<{}>验证要素检查不通过", dstChannelFilterModel.getDstChannelId());
                    }
                }
                String beanKey = completeRouteInfoBean.getDstChannelInfo().getInstId().trim() + ":" + completeRouteInfoBean.getDstChannelInfo().getDstChannelId() + ":" + completeRouteInfoBean.getChannelCallCfg().getTxnNum();
                completeSmartRouteInfo.getAvailDstChannels().put(beanKey, completeRouteInfoBean);
            }
        }

        if (completeSmartRouteInfo.getAvailDstChannels().isEmpty()) {
            rglog.error("无可用通道");
            throw new BizException(SmartRouteRspEnums.NO_AVAILABLE_DST_CHANNEL_ERROR.getRespCode(), SmartRouteRspEnums.NO_AVAILABLE_DST_CHANNEL_ERROR.getRespDesc());
        }
    }


    /**
     * 计算通道所有维度总体得分
     *
     * @param dstChannelInfo 通道信息
     * @param dyncDataStatic 通道动态统计信息
     * @param weightCfg      智能路由配置的各维度的占比
     * @return 通道得分
     */
    private double getTotalScore(DstChannelInfo dstChannelInfo, DyncDataStatic dyncDataStatic, WeightCfg weightCfg) {
        double feePer = getFeePercent(mapFee.get(dyncDataStatic.getChannelId()));
        double avgStressPer = getStressPercent(dstChannelInfo, dyncDataStatic.getAvgStress());
        //使用动态统计数据中的通道成功率,该成功率已经乘以100
        double succRatePer = dyncDataStatic.getSuccRate();
        //使用动态统计数据中的平均响应时间
        double costTimePer = getTimePercent(dstChannelInfo, dyncDataStatic);
        //根据权重计算总分
        double percentTotal = (weightCfg.getFeeCostWeight() / 100.0) * feePer +
                (weightCfg.getStressWeight() / 100.0) * avgStressPer +
                (weightCfg.getSuccRateWeight() / 100.0) * succRatePer +
                (weightCfg.getTimeCostWeight() / 100.0) * costTimePer;
        rglog.debug("通道<{}:{}><费用占比:{}%>,<压力占比:{}%>,<成功率占比:{}%>,<响应时间占比:{}%>",
                dstChannelInfo.getDstChannelId(), dstChannelInfo.getDstChannelName(), weightCfg.getFeeCostWeight(),
                weightCfg.getStressWeight(), weightCfg.getSuccRateWeight(), weightCfg.getTimeCostWeight());
        rglog.debug("通道<{}:{}><费用得分:{}>,<压力得分:{}>,<成功率得分:{}>,<响应时间得分:{}>",
                dstChannelInfo.getDstChannelId(), dstChannelInfo.getDstChannelName(), feePer, avgStressPer, succRatePer, costTimePer);
        return percentTotal;
    }


    /**
     * 计算客户已签约通道
     *
     * @param routerReq 路由请求
     * @return java.util.List<java.lang.String>
     * @throws Exception 失败
     */
    private List<String> calculateSigned(RouterReq routerReq) throws Exception {
        List<String> list = new ArrayList<>();
        String account = "";
        SignAcctInfoMapper signAcctInfoMapper = new SignAcctInfoMapperImpl();
        if (RouteTxnNumEnum.PAYMENT.getTxnGroup().equals(routerReq.getTxnGroup()) || RouteTxnNumEnum.PAYMENT_SIGN.getTxnGroup().equals(routerReq.getTxnGroup())) {
            if (StringUtil.isEmptyOrNull(routerReq.getPayerInfo().getAcctNo()) && StringUtil.isEmptyOrNull(routerReq.getPayerInfo().getSgnNo())) {
                rglog.error("代收,鉴权必须上送付款人信息账号或者签约协议号");
                throw new BizException(SmartRouteRspEnums.MSG_VALID_CHECK_ERROR.getRespCode(), SmartRouteRspEnums.MSG_VALID_CHECK_ERROR.getRespDesc());
            }
            account = routerReq.getPayerInfo().getAcctNo();
        }
        if (RouteTxnNumEnum.CREDIT.getTxnGroup().equals(routerReq.getTxnGroup())) {
            if (StringUtil.isEmptyOrNull(routerReq.getPayeeInfo().getAcctNo())) {
                rglog.error("代付必须上送收款人信息");
                throw new BizException(SmartRouteRspEnums.MSG_VALID_CHECK_ERROR.getRespCode(), SmartRouteRspEnums.MSG_VALID_CHECK_ERROR.getRespDesc());
            }
            account = routerReq.getPayeeInfo().getAcctNo();
        }
        List<SignAcctInfo> listSignAcctInfo = signAcctInfoMapper.selectSignAcctInfoBySignAcctNo(routerReq.getInstId(), account);
        if (listSignAcctInfo == null) {
            return list;
        }
        for (SignAcctInfo signAcctInfo : listSignAcctInfo) {
            if (SmartRouteParamConstant.UPACWTZ.equals(signAcctInfo.getSignTokenType()) ||
                    SmartRouteParamConstant.UPACDS.equals(signAcctInfo.getSignTokenType()) ||
                    SmartRouteParamConstant.UPACDF.equals(signAcctInfo.getSignTokenType())) {
                list.add(SmartRouteParamConstant.UPAC);
            }
            if (SmartRouteParamConstant.NCQSZF.equals(signAcctInfo.getSignTokenType())) {
                list.add(SmartRouteParamConstant.NCQS);
            }
            if (SmartRouteParamConstant.NUCCZF.equals(signAcctInfo.getSignTokenType())) {
                list.add(SmartRouteParamConstant.NUCC);
            }
            if (SmartRouteParamConstant.ESBZF.equals(signAcctInfo.getSignTokenType())) {
                list.add(SmartRouteParamConstant.ESB);
            }
        }
        return list;
    }

    /**
     * 处理得分一致的通道
     *
     * @param listProdInfo 按照优先级从大到小排序后的可能的通道列表
     * @param ruleCfg      路由入口信息
     */
    private void procEqualScore(List<ProdInfoBean> listProdInfo, RuleCfg ruleCfg) {
        if (null != listProdInfo && listProdInfo.size() >= 2) {
            try {
                ProdInfoBean firstProd = listProdInfo.get(0);
                ProdInfoBean secondProd = listProdInfo.get(1);
                double firstPriority = Double.parseDouble(firstProd.getPriority());
                double secondPriority = Double.parseDouble(secondProd.getPriority());
                //头两条得分一致，使用优先级判断顺序
                if (firstPriority - secondPriority <= 0.0005) {
                    SysParamService sysParamService = new SysParamServiceImpl();
                    SysParam sysParam = sysParamService.getSysParamInfo(ruleCfg.getInstId(), SmartRouteParamConstant.ROUTE_CONF_PRIO, ruleCfg.getTxnGroup());
                    //能够查询到数据切数据有效使用配置的优先级，否则不变更现有的顺序
                    if (null != sysParam && SmartRouteParamConstant.YES.equals(sysParam.getParamStatus())) {
                        String proValue = sysParam.getParamValue();
                        rglog.info("优先级为<{}>", proValue);
                        int indexFirst = proValue.indexOf(firstProd.getDstChannelId());
                        int indexSecond = proValue.indexOf(secondProd.getDstChannelId());
                        //第一条的优先级低于第二条,互换位置
                        if (indexFirst > indexSecond) {
                            rglog.info("互换通道<{}:{}>优先级", firstProd.getDstChannelId(), secondProd.getDstChannelId());
                            listProdInfo.set(0, secondProd);
                            listProdInfo.set(1, firstProd);
                        }
                        double highest = Double.parseDouble(firstProd.getPriority()) + 1;
                        listProdInfo.get(0).setPriority(Double.valueOf(highest).toString());
                    }
                }
            } catch (Exception e) {
                rglog.warn(cc.rengu.utility.base.StringUtil.ExceptionToString(e));
            }
        }
    }

    /**
     * TODO 优化
     * 判断通道标识是否在客户已签约列表中
     *
     * @param channelId  通道id
     * @param listSinged 客户已经签约列表
     * @return boolean true该通道客户已经签约，false该通道客户未签约
     **/
    private boolean checkSigned(String channelId, List<String> listSinged) {
        for (String channelIdBak : listSinged) {
            if (channelId.equals(channelIdBak)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 根据通道标识判断验证要素是否齐全
     *
     * @param channelId 通道编码
     * @return boolean true检查通过 false检查不通过
     * @throws BizException 校验异常
     */
    private boolean checkVerificationElements(String channelId, PayerInfo payerInfo) throws BizException {
        //根据渠道标识拼接需要反射的类
        //渠道号首字母大写，其余字母小写
        String className = channelId.substring(0, 1).toUpperCase() + channelId.substring(1).toLowerCase() + SmartRouteParamConstant.CHECKVERELE;
        String path = SmartRouteParamConstant.PACKAGEPATH + channelId.toLowerCase() + "." + className;
        try {
            Class c = Class.forName(path);
            CheckVerificaElement checkVerificaElement = (CheckVerificaElement) c.newInstance();
            return checkVerificaElement.signElementchecked(payerInfo);
        } catch (Exception e) {
            rglog.error("验证要素不全:" + e);
            throw new BizException(SmartRouteRspEnums.TRANS_FAIL.getRespCode(), SmartRouteRspEnums.TRANS_FAIL.getRespDesc());
        }
    }

    /**
     * 根据是否需要签约，签约状态，验证要素返回得分列表
     *
     * @throws Exception 失败跑出异常
     * @author xionglz
     * @date 2020-03-31
     */
    private void calculateSignedCheckVericaElements(RouterReq routerReq) throws Exception {
        //1. 选择出需要签约的通道列表
        List<CompleteRouteInfoBean> needSignList = completeSmartRouteInfo.getAvailDstChannels().values().stream().filter((CompleteRouteInfoBean bean) ->
                bean.getDstTxnAuthCfg().getNeedSignFlag().equals(MustSignEnum.MUST.getCode())).collect(Collectors.toList());
        int needSignedSize = needSignList.size();
        //2. 选择出已经签约的通道列表
        calculateSigned(routerReq);
        List<CompleteRouteInfoBean> signedList = needSignList.stream().filter((CompleteRouteInfoBean bean) ->
                bean.getDstTxnAuthCfg().getNeedSignFlag().equals(SignedStatusEnum.SIGNED.getCode())).collect(Collectors.toList());
        int signedSize = signedList.size();
        List<CompleteRouteInfoBean> unSignedList = needSignList.stream().filter((CompleteRouteInfoBean bean) ->
                !bean.getDstTxnAuthCfg().getNeedSignFlag().equals(SignedStatusEnum.SIGNED.getCode())).collect(Collectors.toList());
        //3. 选择出不需要签约的通道列表
        List<CompleteRouteInfoBean> notNeedSignList = completeSmartRouteInfo.getAvailDstChannels().values().stream().filter((CompleteRouteInfoBean bean) ->
                bean.getDstTxnAuthCfg().getNeedSignFlag().equals(MustSignEnum.NONEED.getCode())).collect(Collectors.toList());
        int notNeedSignedSize = notNeedSignList.size();
        int allDstChannelSize = completeSmartRouteInfo.getAvailDstChannels().size();
        if (allDstChannelSize == needSignedSize && signedSize == 0) {
            //全部需要签约且全部未签约，返回全部列表
            rglog.info("所有通道都需要签约，但是都未签约");
        } else if (allDstChannelSize == needSignedSize) {
            //全部需要签约且存在已签约，返回已签约列表
            rglog.info("所有通道都需要签约，其中签约通道数为<{}>", signedSize);
            //删除需要签约但是未签约的通道列表
            unSignedList.forEach((CompleteRouteInfoBean bean) -> {
                String key = bean.getDstChannelInfo().getInstId().trim() + ":" + bean.getDstChannelInfo().getDstChannelId() + ":" + bean.getChannelCallCfg().getTxnNum();
                completeSmartRouteInfo.getAvailDstChannels().remove(key);
            });

        } else if (allDstChannelSize == notNeedSignedSize) {
            //全部为不需要签约，返回全部列表
            rglog.info("所有通道都不需要签约");
            //TODO 不需要签约的通道都需要校验一边要素是否齐全，不齐全的通道删除

        } else if ((needSignedSize < allDstChannelSize && signedSize > 0)) {
            //部分为需要签约，且存在已签约；部分不需要签约，返回已签约列表+不需要签约列表
            rglog.info("部分通道需要签约，且存在已签约的通道（个数为<{}>)；部分通道不需要签约，返回已签约列表+不需要签约列表", signedSize);
            //删除需要签约但是未签约通道列表
            unSignedList.forEach((CompleteRouteInfoBean bean) -> {
                String key = bean.getDstChannelInfo().getInstId().trim() + ":" + bean.getDstChannelInfo().getDstChannelId() + ":" + bean.getChannelCallCfg().getTxnNum();
                completeSmartRouteInfo.getAvailDstChannels().remove(key);
            });
            //TODO 不需要签约的通道都需要校验一边要素是否齐全，不齐全的通道删除
            //TODO 需要签约但是未签约的通道需要校验一遍要素是否齐全
        } else if ((needSignedSize < allDstChannelSize)) {
            //部分通道需要签约，但是该部分通道都未签约；部分通道不需要签约，返回不需要签约列表+需要签约列表中得分>不需要签约列表中得分最高的
            //此时因为还未计算得分，因此暂不处理，等到计算所有得分之后再处理
            rglog.info("部分通道需要签约，但是该部分通道都未签约；部分通道不需要签约");
            //TODO 不需要签约的通道都需要校验一边要素是否齐全，不齐全的通道删除
            //TODO 需要签约但是未签约的通道需要校验一遍要素是否齐全，不齐全的通道删除
            rglog.info("返回不需要签约列表+需要签约列表中得分>不需要签约列表中得分最高的");
            rglog.info("此时因为还未计算得分，因此暂不处理，等到计算所有得分之后再处理");
//            listNotNeedSigned = listNotNeedSigned.stream().sorted(Comparator.comparing(ProdInfo::getPriority)).collect(Collectors.toList());
//            int maxOfNotNeedSigned = Integer.parseInt(listNotNeedSigned.get(listNotNeedSigned.size()-1).getPriority());
//            for(ProdInfo prodInfo : prodInfoList){
//                if(Integer.parseInt(prodInfo.getPriority()) > maxOfNotNeedSigned){
//                    listFilterProdInfo.add(prodInfo);
//                }
//            }
//            listFilterProdInfo.addAll(listNotNeedSigned);
        } else {
            throw new Exception("不存在应该情况");
        }
        //TODO 若是通道列表大小为0则报无可用通道
    }


    /**
     * 计算成本得分
     *
     * @param nowFee 当前费用
     * @return
     */
    private double getFeePercent(String nowFee) {
        double feePer;
        Double nowFeeD = Double.parseDouble(nowFee);
        int ret = nowFeeD.compareTo(zeroD);
        //当前费用比最低费用的差
        //最低费用为0则不为0的所有的费用得分都为0
        if (feeIsZero && ret != 0) {
            feePer = 0;
        }
        //最低费用为0则为0的所有的费用得分都为100
        else if (feeIsZero) {
            feePer = 100;
        } else {
            feePer = 100.00 - ((nowFeeD - feeMinD) / feeMinD) * 100.00;
        }
        rglog.info("当前费用<{}>,最低费用<{}>,费用得分<{}>", nowFeeD, feeMinD, feePer);
        return feePer;
    }

    /**
     * 计算通道压力得分
     *
     * @param dstChannelInfo 通道信息
     * @param channelCount   通道压力统计值
     * @return
     */
    private double getStressPercent(DstChannelInfo dstChannelInfo, int channelCount) {
        //如果基准值即最大支持交易笔数为0则为未配置，设置基准的压力值为100
        int baseStress = dstChannelInfo.getMaxSupportNum();
        if (0 >= baseStress) {
            baseStress = 100;
        }
        double stressPer = 100;
        int max = (int) (baseStress * 0.8);
        if (channelCount > max) {
            stressPer = 100 - ((double) (channelCount - max) / (double) max) * 100.00;
        }
        rglog.debug("基准压力值:<{}>, 当前统计压力值<{}>, 压力单项分数<{}>", baseStress, channelCount, stressPer);
        return stressPer;
    }

    /**
     * 计算响应时间得分
     *
     * @param dstChannelInfo 通道信息
     * @param dyncDataStatic 动态统计数据
     * @return
     */
    private double getTimePercent(DstChannelInfo dstChannelInfo, DyncDataStatic dyncDataStatic) {
        //通道响应时间单位为秒
        int dstChannelTO = dstChannelInfo.getChannelTimeout();
        //未配置通道的响应时间则设置默认的基准时间为60秒
        if (0 == dstChannelTO) {
            dstChannelTO = 60;
        }
        rglog.debug("平均响应时间:<{}>, 超时时间<{}>, 响应时间单项分数<{}>", dyncDataStatic.getAvgTimeCost(), dstChannelTO, 100.00 - ((double) (dstChannelTO * 1000 - dyncDataStatic.getAvgTimeCost()) / ((double) dstChannelTO * 1000)) * 100.00);
        //统计值的响应时间单位为毫秒，统一单位为毫秒
        return 100.00 - ((double) (dstChannelTO * 1000 - dyncDataStatic.getAvgTimeCost()) / ((double) dstChannelTO * 1000)) * 100.00;
    }

    /**
     * 获取map中值最小的值
     *
     * @return java.lang.Double
     **/
    private String getMinValue() {
        Collection<String> c = mapFee.values();
        Object[] obj = c.toArray();
        Arrays.sort(obj);
        return obj[0].toString();
    }

    /**
     * 计算每个通道的费用
     *
     * @param amt 交易金额
     * @throws Exception 失败
     */
    private void calculateCost(String amt) throws Exception {
        mapFee = new HashMap<>();
        //如果不上送交易金额则所有通道的费用都为0，费用得分都为100分
        FeeInfoMapper feeInfoMapper = new FeeInfoMapperImpl();
        CalcMchntFee calcMchntFee = new CalcMchntFee();
        DatabaseOpr database = new DatabaseOpr();
        Connection connection = database.getDbConnection();
        if (connection == null) {
            rglog.error("获取数据库链接失败");
            throw new BizException(SmartRouteRspEnums.TRANS_FAIL.getRespCode(), SmartRouteRspEnums.TRANS_FAIL.getRespDesc());
        }
        try {
            for (CompleteRouteInfoBean completeRouteInfoBean : completeSmartRouteInfo.getAvailDstChannels().values()) {
                rglog.warn("计算通道交易<{}：{}：{}>费用", completeRouteInfoBean.getChannelCallCfg().getInstId(), completeRouteInfoBean.getChannelCallCfg().getCallChannelId(), completeRouteInfoBean.getChannelCallCfg().getCallChannelTxn());
                if (cc.rengu.utility.base.StringUtil.isNullorEmpty(amt)) {
                    mapFee.put(completeRouteInfoBean.getChannelCallCfg().getCallChannelId(), "0.000");
                    continue;
                }
                if (0 == AmountUtil.compare(amt, "0")) {
                    mapFee.put(completeRouteInfoBean.getChannelCallCfg().getCallChannelId(), "0.000");
                    continue;
                }
                //未配置费率模板费率以0计算
                if (StringUtil.isEmptyOrNull(completeRouteInfoBean.getDstTxnAuthCfg().getChannelTxnRate())) {
                    rglog.info("通道交易<{}:{}:{}>未配置费率费用直接以0计算", completeRouteInfoBean.getChannelCallCfg().getInstId(), completeRouteInfoBean.getChannelCallCfg().getCallChannelId(), completeRouteInfoBean.getChannelCallCfg().getCallChannelTxn());
                    mapFee.put(completeRouteInfoBean.getChannelCallCfg().getCallChannelId(), "0.000");
                } else {
                    //查询费率模板表
                    List<FeeInfoObj> listFeeInfoObj = feeInfoMapper.getFeeInfoByFeeCode(completeRouteInfoBean.getDstTxnAuthCfg().getChannelTxnRate(), DateUtil.getCurrentDate(), completeRouteInfoBean.getChannelCallCfg().getInstId(), connection);
                    Optional.ofNullable(listFeeInfoObj).orElseThrow(() -> {
                        rglog.error("费率模板表未配置,<Feeid:{}>", completeRouteInfoBean.getDstTxnAuthCfg().getChannelTxnRate());
                        return new BizException(SmartRouteRspEnums.CONFIG_ERROR.getRespCode(), SmartRouteRspEnums.CONFIG_ERROR.getRespDesc());
                    });
                    //计算费用
                    String fee = "0.00";    //计算手续费包替换临时修改
//                    String fee = calcMchntFee.feeCdCharging(amt, listFeeInfoObj);
//                    if (StringUtil.isEmptyOrNull(fee)) {
//                        rglog.error("费用计算失败,<Feeid:{}>", completeRouteInfoBean.getDstTxnAuthCfg().getChannelTxnRate());
//                        throw new BizException(SmartRouteRspEnums.TRANS_FAIL.getRespCode(), SmartRouteRspEnums.TRANS_FAIL.getRespDesc());
//                    }
                    rglog.info("通道<{}>,交易<{}>,费用模板<{}>,交易金额<{}>,费用<{}>", completeRouteInfoBean.getChannelCallCfg().getCallChannelId(),
                            completeRouteInfoBean.getChannelCallCfg().getCallChannelTxn(), completeRouteInfoBean.getDstTxnAuthCfg().getChannelTxnRate(), amt, fee);
                    mapFee.put(completeRouteInfoBean.getChannelCallCfg().getCallChannelId(), fee);
                }
            }
        } finally {
            ConnectDB.closeConn(connection);
        }

        //所有通道都被滤掉
        if (mapFee.isEmpty()) {
            rglog.error("所有通道都被滤掉,交易失败");
            throw new BizException(SmartRouteRspEnums.CONFIG_ERROR.getRespCode(), SmartRouteRspEnums.CONFIG_ERROR.getRespDesc());
        }
        if (completeSmartRouteInfo.getAvailDstChannels().isEmpty()) {
            rglog.error("所有通道都被滤掉,交易失败");
            throw new BizException(SmartRouteRspEnums.CONFIG_ERROR.getRespCode(), SmartRouteRspEnums.CONFIG_ERROR.getRespDesc());
        }
        //得到最小值
        String feeMin = getMinValue();
        feeMinD = Double.parseDouble(feeMin);
        rglog.info("最低费用为<{}>", feeMinD);
        //最低手续费为0的情况
        if (feeMinD.compareTo(zeroD) == 0) {
            feeIsZero = true;
        }
    }

    /**
     * 获取所有可用通道的得分
     *
     * @param routerReq 路由查询请求
     * @return 产品列表
     * @throws Exception 异常
     */
    private List<ProdInfoBean> getAllScore(RouterReq routerReq) throws Exception {
        List<ProdInfoBean> listProdInfo = new ArrayList<>();
        //循环计算每条通道的得分
        for (CompleteRouteInfoBean bean : completeSmartRouteInfo.getAvailDstChannels().values()) {
            //判断系统缓存中是否有该条通道的动态统计数据,系统加载时创建所有通道Redis缓存的动态数据
            int channelCount = 0;
            //为保证系统内存中动态数据统计的正确
            String redisKey = AppParamConstant.DEFAULT_INSTID + ":" + bean.getDstChannelInfo().getDstChannelId();
            String nodeKey = redisKey + ":" + IPv4Util.ipv4Address;
            rglog.info("cc.rengu.igas.route.core.realize.impl.WeightCfgRouterChoiceImpl   nodeKey<{}>", nodeKey);
            DyncDataStatic dyncDataStatic = (DyncDataStatic) UnifiedCache.get(SmartRouteParamConstant.ROUTE_DATA_SMART, nodeKey);
            if (null == dyncDataStatic) {
                //存在从静态路由修改为智能路由的情况，此时即使刷新缓存也只能刷新到随机的单个节点，因此需要交易的时候初始化一次
                //初始化涉及到并发问题，因此需要全局锁
                RouterDataInitSmartDyncData routerDataInitSmartDyncData = new RouterDataInitSmartDyncData();
                if (!RouterDataInitSmartDyncData.isUpdate()) {
                    routerDataInitSmartDyncData.loadRouterData();
                }
                //若正在更新,则同步等待更新完成,最大等待时间3秒，3秒后若无数据则失败
                else {
                    routerDataInitSmartDyncData.waitload();
                }
                dyncDataStatic = (DyncDataStatic) UnifiedCache.get(SmartRouteParamConstant.ROUTE_DATA_SMART, nodeKey);
                if (null == dyncDataStatic) {
                    rglog.error("智能路由初始化之后仍然未查询到动态统计数据");
                    throw new BizException(SmartRouteRspEnums.CONFIG_ERROR.getRespCode(), SmartRouteRspEnums.CONFIG_ERROR.getRespDesc());
                }
            }
            dyncDataStatic.setUpdateTime(DateUtil.getCurrentDateTime(SmartRouteParamConstant.FORMAT_DATE_TIME19));
            //计算通道压力方法 计数器中当前通道的排队数量/通道最大交易并发数
            String countRedis = null;
            try {
                String redisStressKey = SmartRouteParamConstant.ROUTE_DATA_ALL_STRESS + ":" + redisKey;
                countRedis = RedisUtil.onceGet(redisStressKey);
            } catch (Exception e) {
                rglog.warn(e.getMessage());
            }
            //获取到通道压力 未获取到通道压力以通道无压力计算
            if (!StringUtil.isEmptyOrNull(countRedis)) {
                try {
                    channelCount = Integer.parseInt(countRedis);
                } catch (Exception e) {
                    rglog.warn(e.getMessage());
                    channelCount = 0;
                }
            }
            dyncDataStatic.setAvgStress(channelCount);
            rglog.debug("通道<{}>压力为<{}>", bean.getDstChannelInfo().getDstChannelId(), channelCount);

            //获取到通道整体成功率
            String succRate = null;
            int succRateInt = 100;
            try {
                String redisSuccRateKey = SmartRouteParamConstant.ROUTE_DATA_ALL_SUCCRATE + ":" + redisKey;
                succRate = RedisUtil.onceGet(redisSuccRateKey);
            } catch (Exception e) {
                rglog.warn(e.getMessage());
            }
            if (!StringUtil.isEmptyOrNull(succRate)) {
                try {
                    succRateInt = Integer.parseInt(succRate);
                } catch (Exception e) {
                    rglog.warn(e.getMessage());
                    succRateInt = 100;
                }
            }
            dyncDataStatic.setSuccRate(succRateInt);
            rglog.debug("通道<{}>成功率为<{}>", bean.getDstChannelInfo().getDstChannelId(), succRateInt);

            //获取通道平均响应时间
            long currentTime = DateUtil.getCurrentTimeStamp();
            long rangeTime = currentTime - 5 * 60 * 1000;
            List<String> transTimeList = new ArrayList<>();
            List<String> transTimeNodeList = new ArrayList<>();
            String transTimePreKey = SmartRouteParamConstant.ROUTE_DATA_ALL_TIMECOST + ":" + redisKey;
            String transTimeNodePreKey = SmartRouteParamConstant.ROUTE_DATA_ALL_TIMECOST + ":" + nodeKey;
            while (rangeTime < currentTime) {
                String dateTime = DateUtil.getDateByTimeStamp(rangeTime, "yyyyMMddHHmmss").substring(0, 13);
                String transTimeKey = transTimePreKey + ":" + dateTime;
                List<String> transTimeSplitList = RedisUtil.getListRange(transTimeKey, 0, -1);
                if (null != transTimeSplitList && !transTimeSplitList.isEmpty()) {
                    rglog.debug("获取通道平均响应时间 transTimeSplitList.size<{}>", transTimeSplitList.size());
                    transTimeList.addAll(transTimeSplitList);
                }

                String transTimeNodeKey = transTimeNodePreKey + ":" + dateTime;
                List<String> transTimeSplitNodeList = RedisUtil.getListRange(transTimeNodeKey, 0, -1);
                if (null != transTimeSplitNodeList && !transTimeSplitNodeList.isEmpty()) {
                    transTimeNodeList.addAll(transTimeSplitNodeList);
                }

                rangeTime += 10 * 1000;
            }

            if (!transTimeList.isEmpty()) {
                rglog.debug("获取通道平均响应时间 transTimeList.size<{}>", transTimeList.size());
                OptionalDouble optionalDouble = transTimeList.stream().mapToInt(Integer::valueOf).average();
                //使用当前平均值
                if (optionalDouble.isPresent()) {
                    rglog.debug("获取通道平均响应时间 平均值<{}>", Double.valueOf(optionalDouble.getAsDouble()).intValue());
                    dyncDataStatic.setAvgTimeCost(Double.valueOf(optionalDouble.getAsDouble()).intValue());
                }
                //else使用数据库获取到的值

                OptionalDouble optionalDoubleNode = transTimeNodeList.stream().mapToInt(Integer::valueOf).average();
                //使用当前平均值
                if (optionalDoubleNode.isPresent()) {
                    dyncDataStatic.setNodeAvgTimeCost(Double.valueOf(optionalDoubleNode.getAsDouble()).intValue());
                }
                //else使用数据库获取到的值
            } else {
                rglog.debug("获取通道平均响应时间 transTimeList.size<{}>", "0");
            }
            //else 使用数据库获取到的值

            double score = getTotalScore(bean.getDstChannelInfo(), dyncDataStatic, completeSmartRouteInfo.getWeightCfg());
            //将智能路由动态统计数据入系统缓存
            UnifiedCache.set(SmartRouteParamConstant.ROUTE_DATA_SMART, nodeKey, dyncDataStatic);
            ProdInfoBean prodInfo = new ProdInfoBean();
            prodInfo.setDelayedFlag(routerReq.getDelayedFlag());
            prodInfo.setDstChannelId(bean.getDstChannelInfo().getDstChannelId());
            prodInfo.setFee(mapFee.get(bean.getDstChannelInfo().getDstChannelId()));
            prodInfo.setNeedSign(bean.getDstTxnAuthCfg().getNeedSignFlag());
            prodInfo.setPriority(String.valueOf(score));
            prodInfo.setProdDesc(bean.getDstTxnAuthCfg().getTxnName());
            prodInfo.setProdSts(bean.getDstChannelInfo().getDstChannelStatus());
//            prodInfo.setSignSt();
            prodInfo.setDstChannelTxnImplClassName(bean.getDstChannelTxnImplClassName());
            prodInfo.setDstChannelTxnNum(bean.getDstTxnAuthCfg().getChannelTxn());
            listProdInfo.add(prodInfo);
        }
        return listProdInfo;
    }
}
